package com.supcon.tea.work.questionnaire.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.supcon.tea.common.Constant;
import com.supcon.tea.common.Recode;
import com.supcon.tea.work.questionnaire.dao.QuestionnaireDao;
import com.supcon.tea.work.questionnaire.domain.*;
import com.supcon.tea.work.questionnaire.domain.dto.AnswerDTO;
import com.supcon.tea.work.questionnaire.domain.dto.UserPhysiqueDTO;
import com.supcon.tea.work.questionnaire.domain.dto.QuestionnaireDTO;
import com.supcon.tea.work.questionnaire.domain.vo.AnswerVO;
import com.supcon.tea.work.questionnaire.domain.vo.UserPhysiqueVO;
import com.supcon.tea.work.questionnaire.service.QuestionnaireService;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.PostConstruct;
import java.text.DecimalFormat;
import java.util.*;
import java.util.stream.Collectors;

/**
 * Created by zhengjian1 on 2017/7/21.
 */
@Service
public class QuestionnaireServiceImpl implements QuestionnaireService{
    @Autowired
    QuestionnaireDao questionnaireDao;
    @Autowired
    private RedisTemplate redisTemplate;

    ValueOperations<String, Object> operations;

    private Map<Integer,String> physiqueMap = new HashMap<>();
    @PostConstruct
    private void init() {
        operations = redisTemplate.opsForValue();
    }
    @Override
    public QuestionnaireDTO getQuestionnaire(String userId,Integer type, Integer sex){
        QuestionnaireDTO questionnaireDTO = new QuestionnaireDTO();
        Questionnaire questionnaire = (Questionnaire)operations.get(Constant.PREFIX_REDIS_QUESTIONNAIRE_KEY + type);
        if(questionnaire==null){
            return null;
        }
        BeanUtils.copyProperties(questionnaire,questionnaireDTO);
        List<Question> questions = questionnaire.getQuestionIds().stream()
                .map(id->(Question)operations.get(Constant.PREFIX_REDIS_QUESTION_KEY + id))
                .filter(question ->0==question.getSex()||sex==question.getSex())
                .collect(Collectors.toList());
        questionnaireDTO.setQuestions(questions);
        questionnaireDTO.setNums(questions.size());
        if(userId!=null&&userId.length()>0) {
            Object redisObj = operations.get(this.getRedisAnswerKey(userId, questionnaireDTO.getId()));
            questionnaireDTO.setAnswers(redisObj!=null?(List<AnswerVO>)redisObj:null);
        }
        return questionnaireDTO;
    }

    @Override
    public JSONObject fixAnswer(AnswerDTO answerDTO){
        JSONObject result = new JSONObject();
        UserPhysiqueVO userPhysiqueVO = new UserPhysiqueVO();
        Double rate;
        rate  =  Double.parseDouble(new DecimalFormat("#.00").format((float)answerDTO.getAnswers().size()/answerDTO.getNums()));
        UserPhysique userPhysique = new UserPhysique();
        Map<Integer,Integer> scoreMap = new HashMap<>();
        Map<Integer ,Integer> nums = new HashMap<>();
        this.fixAnswer(scoreMap,nums,answerDTO);
        BeanUtils.copyProperties(answerDTO, userPhysique);
        if(rate==1){
            this.clearRedisAnswerKey(answerDTO.getUserId(),answerDTO.getQuestionnaireId());
            UserPhysiqueDTO userPhysiqueDTO = this.fixPhysique(scoreMap,nums);
            Physique physique = (Physique)operations.get(Constant.PREFIX_REDIS_PHYSIQUE_KEY + userPhysiqueDTO.getPhysiqueType());
            userPhysiqueDTO.setRate(rate);
            BeanUtils.copyProperties(physique, userPhysiqueVO);
            BeanUtils.copyProperties(userPhysiqueDTO, userPhysique);
            BeanUtils.copyProperties(answerDTO, userPhysique);
            if(postAnswers(answerDTO.getAnswers(), userPhysique)){
                result.put("recode", Recode.SUCCESS);
                result.put("msg","成功");
                result.put("data", userPhysiqueVO);
            }else {
                result.put("recode",Recode.ERROR);
                result.put("msg","保存答案失败，解析体质成功");
                result.put("data", userPhysiqueVO);
            }
        }else{
            operations.set(this.getRedisAnswerKey(answerDTO.getUserId(),answerDTO.getQuestionnaireId()),answerDTO.getAnswers());
            result.put("recode",Recode.SUCCESS);
            result.put("msg","保存当前答题情况");
            result.put("data", rate);
        }
        return result;
    }


    private int fixScore(int score,int num){
        return 100*(score-num)/(num*4);
    }

    /**
     * 计算各个体质的分数
     * @param scoreMap
     * @param nums
     * @param answerDTO
     */
    private void fixAnswer(Map<Integer,Integer> scoreMap, Map<Integer ,Integer> nums,AnswerDTO answerDTO){
        answerDTO.getAnswers().stream().forEach(
                answer -> {
                    Question question = (Question) operations.get(Constant.PREFIX_REDIS_QUESTION_KEY + answer.getQuestionId());
                    int id = question.getPhysiqueType();
                    int mscore = question.getOptions().stream().filter(option -> answer.getOptionKey()==option.getKey()).findFirst().get().getScore();
                    if(scoreMap.containsKey(id)){
                        int score = scoreMap.get(id);
                        int num = nums.get(id);
                        scoreMap.put(id, mscore + score);
                        nums.put(id, num + 1);
                    }
                    if(!scoreMap.containsKey(id)){
                        scoreMap.put(id, mscore);
                        nums.put(id, 1);
                    }
                }
        );
    }

    /**
     * 得出体质结果
     * @param scoreMap
     * @param nums
     * @return
     */
    private UserPhysiqueDTO fixPhysique(Map<Integer,Integer> scoreMap, Map<Integer ,Integer> nums){
        UserPhysiqueDTO userPhysiqueDTO = new UserPhysiqueDTO();
        List<Integer> surePhysique = new ArrayList<>();
        for (Map.Entry<Integer, Integer> entry : scoreMap.entrySet()){
            int id = entry.getKey();
            int score = fixScore(entry.getValue(),nums.get(id));
            entry.setValue(score);
            if(score>=40){
                surePhysique.add(id);
            }
        }
        if(surePhysique.size()>0){
            boolean isGentle = true;
            for(int id:surePhysique){
                if(id != 9){
                    userPhysiqueDTO.setPhysiqueType(id);
                    isGentle = false;
                    break;
                }
            }
            if(isGentle){
                userPhysiqueDTO.setPhysiqueType(9);
            }
        }else{
            userPhysiqueDTO.setPhysiqueType(9);
        }
        return userPhysiqueDTO;
    }

    /**
     * 保存用户答案数据
     * @param answers
     * @param userPhysique
     * @return
     */
    @Transactional(propagation= Propagation.REQUIRED,isolation = Isolation.READ_COMMITTED)
    private boolean postAnswers(List<AnswerVO> answers,UserPhysique userPhysique){
        boolean isSuccess = true;
        try {
            Integer existNum = questionnaireDao.isPhysique(userPhysique.getUserId(), userPhysique.getQuestionnaireId());
            existNum = existNum == null ? 1 : (existNum + 1);
            userPhysique.setExistNum(existNum);
            questionnaireDao.postPhysique(userPhysique);
            answers.stream().forEach(answerVO -> {
                Answer answer = new Answer();
                BeanUtils.copyProperties(answerVO,answer);
                answer.setUserPhysiqueId(userPhysique.getId());
                questionnaireDao.postAnswer(answer);
            });
        }catch (Exception e){
            isSuccess = false;
            e.printStackTrace();
        }
        return isSuccess;
    }

    private String getRedisAnswerKey(String userId,Integer questionnaireType){
        return Constant.PREFIX_REDIS_UN_ANSWER_KEY + userId + "_" + questionnaireType;
    }

    private void clearRedisAnswerKey(String userId,Integer questionnaireType){
        String key = getRedisAnswerKey(userId,questionnaireType);
        operations.getOperations().delete(key);
    }


    class AnswerThread implements Runnable{
        private UserPhysique userPhysique;
        private List<Answer> answers;
        AnswerThread(UserPhysique userPhysique, List<Answer> answers){
            this.userPhysique = userPhysique;
            this.answers = answers;
        }
        public void run(){
            try {
                Thread.sleep(3000);
                questionnaireDao.getOptions(1);
                System.out.println("启动线程是为了处理比较大的数据");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
